package service

import (
	"errors"
	"fmt"
	"github.com/cihub/seelog"
	"shop/config"
	"shop/config/constant"
	"shop/dao"
	"shop/entity"
	"shop/utils/convertutil"
	"shop/utils/genutil"
	"shop/utils/httputil"
	"shop/utils/result"
	"shop/utils/sessionutil"
	"shop/utils/weixinutil"
	"strings"
)

// LoginByWeixin 微信授权登录
func LoginByWeixin(shopId, loginType int, weixinCode string) *result.Result {
	shop := GetShopById(shopId)
	if shop == nil || shop.Id == 0 {
		return result.Fail("商家不存在")
	}

	if loginType == 1 { // 微信扫码直接授权登录（公众号）
		state := genutil.RandStr(30)
		url := fmt.Sprintf(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=%s#wechat_redirect`,
			shop.WxGzhAppId, fmt.Sprintf(config.WeixinLoginGzhAuthCodeHandleApiUrl, shopId), state)
		return result.Ok(map[string]interface{}{
			"state":     state,
			"h5_url":    url,
			"qrcodeImg": convertutil.StrToBase64Img(url),
		})
	} else if loginType == 2 { // 微信网页登录（公众号）
		url := fmt.Sprintf(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=weixin_h5#wechat_redirect`,
			shop.WxGzhAppId, fmt.Sprintf(config.WeixinLoginGzhAuthCodeHandleApiUrl, shopId))
		return result.Ok(url)
	} else if loginType == 3 { // 微信小程序登录
		openid := weixinutil.GetWxMpOpenid(shop.WxMpAppId, shop.WxMpAppSecret, weixinCode)
		if openid == "" {
			return result.Fail("获取微信openid失败")
		}

		user := dao.GetUserByShopIdAndUsername(shopId, openid)
		if user == nil || user.Id == 0 {
			user = &entity.User{
				ShopId:       shopId,
				Username:     openid,
				RegisterType: constant.RegisterTypeWxMp,
			}
			if !registerUser(user) {
				return result.Fail("用户注册失败")
			}
		} else if user.Status != constant.UserStatusNormal {
			return result.Fail("账号异常，" + constant.UserStatusCN(user.Status))
		}
		return result.Ok(sessionutil.SetUserSession(user))
	}
	return result.Fail(fmt.Sprintf("不支持的微信登录类型: %d", loginType))
}

// WeixinH5Auth 通过微信扫码后获取的公众号授权码登录. 返回token
func WeixinH5Auth(shopId int, wxGzhCode string) (string, error) {
	shop := GetShopById(shopId)
	if shop == nil || shop.Id == 0 {
		return "", errors.New("商家不存在")
	}

	openid := weixinutil.GetWxGzhOpenid(shop.WxGzhAppId, shop.WxGzhAppSecret, wxGzhCode)
	user := dao.GetUserByShopIdAndUsername(shopId, openid)
	if user == nil || user.Id == 0 {
		user = &entity.User{
			ShopId:       shopId,
			Username:     openid,
			RegisterType: constant.RegisterTypeWxGzh,
		}
		if !registerUser(user) {
			return "", errors.New("用户注册失败")
		}
	} else if user.Status != constant.UserStatusNormal {
		return "", errors.New("账号异常，" + constant.UserStatusCN(user.Status))
	}
	return sessionutil.SetUserSession(user), nil
}

// TencentAuthLogin 腾讯QQ-auth2.0授权登录
func TencentAuthLogin(shopId int, code string) (redirectUrl string, err error) {
	defer seelog.Infof("腾讯授权登录, shopId: %d, code: %s, url: %s, err: %v", shopId, code, redirectUrl, err)
	shop := GetShopById(shopId)
	if shop == nil || shop.Id == 0 {
		err = errors.New("商家不存在")
		return
	}
	// 去腾讯授权登录页面获取授权码
	if code == "" {
		redirectUrl = fmt.Sprintf(`https://graph.qq.com/oauth2.0/authorize?client_id=%s&client_secret=%s&response_type=code&state=code&redirect_uri=%s`,
			config.Conf.TencentAuth20AppId, config.Conf.TencentAuth20AppKey, fmt.Sprintf(config.TencentAuth20ReciredtUri, shop.Id))
		return redirectUrl, nil
	}
	// 去获取token
	redirectUrl = fmt.Sprintf(`https://graph.qq.com/oauth2.0/token?client_id=%s&client_secret=%s&grant_type=authorization_code&code=%s&redirect_uri=%s`,
		config.Conf.TencentAuth20AppId, config.Conf.TencentAuth20AppKey, code, fmt.Sprintf(config.TencentAuth20ReciredtUri, shop.Id))
	authRes := httputil.Get(redirectUrl) // 这里腾讯应该会跳转到我们指定的url，但是测试多次都是直接返回html，并没有跳转。。。很奇怪,可能已经改了（那也应该返回json才对），离谱
	// eg: access_token=065CD9EE69CC700357E1FCA57893329C&expires_in=7776000&refresh_token=773F4070D44DB70B69DC7DCDA3D79412
	if strings.Index(authRes, "access_token") < 0 {
		return "", errors.New("access_token获取失败: " + authRes)
	}

	accessToken := authRes[len("access_token="):strings.Index(authRes, "&")]
	// 通过access_token获取openid直接登录
	resJson := httputil.Get(`https://graph.qq.com/oauth2.0/me?access_token=` + accessToken + "&fmt=json")
	res := struct {
		ClientId string `json:"client_id"`
		Openid   string `json:"openid"`
	}{}
	convertutil.ParseJson(resJson, &res)
	if res.Openid == "" {
		return "", errors.New("获取openid失败") // 接口调用有错误时，会返回code和msg字段，以url参数对的形式返回，value部分会进行url编码（UTF-8）。
	}

	user := dao.GetUserByShopIdAndUsername(shopId, res.Openid)
	if user == nil || user.Id == 0 {
		user = &entity.User{
			ShopId:       shopId,
			Username:     res.Openid,
			RegisterType: constant.RegisterTypeTencent,
		}
		if !registerUser(user) {
			return "", errors.New("用户注册失败")
		}
	} else if user.Status != constant.UserStatusNormal {
		return "", errors.New("账号异常，" + constant.UserStatusCN(user.Status))
	}
	return config.LoginByTokenRedirectUiUrl + sessionutil.SetUserSession(user), nil
}

// LoginByPhoneAndCode 手机+验证码登录
func LoginByPhoneAndCode(shopId int, phone, code string) *result.Result {
	if !CheckShopId(shopId) {
		return result.Fail("商家不存在")
	}

	if phone == config.DevPhone && code == config.DevPhoneVerifyCode { // 测试号登录
		user := dao.GetUserByShopIdAndUsername(shopId, phone)
		if user == nil || user.Id == 0 {
			return result.Fail("测试用户不存在")
		}
		return result.Ok(sessionutil.SetUserSession(user))
	}

	cacheK := constant.CacheKeyPrefixVerifyCodeUserLogin + phone // 一个phone对应一个code
	realCode := dao.GetRedisCli().Get(cacheK).Val()
	if realCode == "" {
		return result.Fail("验证码不存在或已经过期")
	}
	if realCode != code {
		return result.Fail("验证码不正确")
	}
	dao.GetRedisCli().Del(cacheK)

	user := dao.GetUserByShopIdAndUsername(shopId, phone)
	if user == nil || user.Id == 0 {
		user = &entity.User{
			ShopId:       shopId,
			Username:     phone,
			Phone:        phone,
			RegisterType: constant.RegisterTypePhone,
		}
		if !registerUser(user) {
			return result.Fail("用户注册失败")
		}
	} else if user.Status != constant.UserStatusNormal {
		return result.Fail("账号异常，" + constant.UserStatusCN(user.Status))
	}
	return result.Ok(sessionutil.SetUserSession(user))
}

func registerUser(user *entity.User) bool {
	user.Name = genutil.RandStr(4)
	user.Status = constant.UserStatusNormal
	if err := dao.GetTx().Model(user).Create(user).Commit().Error; err != nil {
		seelog.Errorf("fail to crete user, err: %v", err)
		return false
	}
	seelog.Debugf("auto register user, user: %v", user)
	return true
}
